package alp.starcode.sanping.framework.global;

import alp.starcode.common.base.constant.BaseResultDataConstant;
import alp.starcode.common.base.exception.AccessAuthorityException;
import alp.starcode.common.base.exception.BaseException;
import alp.starcode.common.base.exception.ValidatedException;
import alp.starcode.common.web.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * 全局异常捕获类
 *
 * @author liwang
 * @date 2021年11月19日10:02:43
 */
@Slf4j
@RestControllerAdvice
@Component
public class ControllerExceptionAdvice {

    /**
     * 设置异常信息
     *
     * @param result 返回结果
     * @param e      异常
     */
    private void setException(Result<Object> result, Exception e) {
        if (e instanceof BaseException) {
            if (((BaseException) e).isPrintStackTrace()) {
                log.error("全局异常", e);
            }
        } else {
            log.error("全局异常", e);
        }
        result.setError(e.getMessage());
    }

    /**
     * 所有异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Result<Object> exception(Exception e) {
        if (e.getCause() instanceof BaseException) {
            return baseException((BaseException) e.getCause());
        } else {
            Result<Object> result = new Result<>(BaseResultDataConstant.ERROR);
            setException(result, e);
            return result;
        }
    }

    /**
     * 基础处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = BaseException.class)
    public Result<Object> baseException(BaseException e) {
        Result<Object> result = new Result<>();
        result.setCode(e.getCode());
        result.setMsg(e.getMsg());
        setException(result, e);
        return result;
    }

    /**
     * 参数验证异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({BindException.class})
    public Result<Object> bindException(BindException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        String errorMsg = objectError.getDefaultMessage();
        String fieldName = "";
        Object[] arguments = objectError.getArguments();
        if (null != arguments && arguments.length > 0) {
            Object argument = arguments[0];
            if (argument instanceof DefaultMessageSourceResolvable) {
                fieldName = ((DefaultMessageSourceResolvable) argument).getDefaultMessage();
            }
        }
        Result<Object> result = new Result<>(BaseResultDataConstant.PARAM_VERIFY_ERROR, errorMsg);
        result.setData("[" + fieldName + "]" + errorMsg);
        setException(result, e);
        return result;
    }

    /**
     * 参数验证异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public Result<Object> constraintViolationException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));
        Result<Object> result = new Result<>(BaseResultDataConstant.PARAM_VERIFY_ERROR, message);
        result.setData(message);
        setException(result, e);
        return result;
    }

    /**
     * 主键异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = DuplicateKeyException.class)
    public Result<Object> duplicateKeyException(DuplicateKeyException e) {
        Result<Object> result = new Result<>();
        result.setCode(BaseResultDataConstant.DATA_IS_EXISTS.getCode());
        result.setMsg(BaseResultDataConstant.DATA_IS_EXISTS.getMsg());
        setException(result, e);
        return result;
    }


    /**
     * 访问权限异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = AccessAuthorityException.class)
    public Result<Object> accessAuthorityException(AccessAuthorityException e) {
        Result<Object> result = new Result<>();
        result.setCode(e.getCode());
        result.setMsg(e.getMsg());
        setException(result, e);
        return result;
    }


    /**
     * 参数校验异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = ValidatedException.class)
    public Result<Object> validatedException(ValidatedException e) {
        Result<Object> result = new Result<>();
        result.setCode(e.getCode());
        result.setMsg(e.getMsg());
        setException(result, e);
        return result;
    }

}
